package sf.database.meta;

import sf.core.DBCascadeField;
import sf.core.DBField;
import sf.spring.util.LinkedCaseInsensitiveMap;
import sf.tools.reflectasm.ConstructorAccess;
import sf.tools.reflectasm.MethodAccess;

import javax.persistence.Table;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 代表实体类对象的相关信息
 */
public class TableMapping implements ITableMapping {
    protected String schema;
    protected String catalog;
    protected String tableName;
    //    protected List<UniqueConstraint> uniques;
    //    protected List<Index> indexs;
    protected Table table;
    private String comment;// 注释

    /**
     *
     */
    protected Map<String, String> tableMetaMap = Collections.emptyMap();
    /**
     * 实体类的全部字段(包含非@Column注解的字段)
     */
    protected final List<ColumnMapping> metaFields = new ArrayList<>();

    /**
     * java字段名称和字段对应,支持实体类里出现只用大小写区分的同类型对象.
     */
    protected final Map<String, ColumnMapping> metaFieldMap = new HashMap<>();

    /**
     * 不区分大小写,数据库列名和字段对应,支持实体类里出现只用大小写区分的同类型对象.
     */
    protected final LinkedCaseInsensitiveMap<ColumnMapping> columnFieldMap = new LinkedCaseInsensitiveMap<>();

    /**
     * 有DBField字段的Mapping,代表和数据库字段一一对应
     */
    protected final Map<DBField, ColumnMapping> schemaMap = new LinkedHashMap<>();
    /**
     * schemaMap 的list,主要提供快速索引
     */
    protected final List<ColumnMapping> schemas = new ArrayList<>();

    // java类枚举名称(字段名称)到field对应
    protected final Map<String, DBField> fields = new LinkedHashMap<>(8, 0.75f);
    // 小写

    /**
     * 级联字段描述,字段名称-->级联字段枚举
     */
    protected Map<String, DBCascadeField> cascadeFields = Collections.emptyMap();

    /**
     * 乐观锁 @Version字段的快速索引.
     * @see javax.persistence.Version
     */
    protected Map<DBField, ColumnMapping> versionMap = Collections.emptyMap();

    protected boolean cacheable;

    /**
     * 记录当前Schema所对应的实体类。
     */
    private Class<?> thisType;

    /**
     * 特殊列,如自动添加创建时间和修改时间
     */
    private List<ColumnMapping> specialFields = Collections.emptyList();

    private List<ColumnMapping> pkFields = Collections.emptyList();// 记录主键列
    // 提供Field到列名的转换
    // 提供Column名称到Field的转换，不光包括元模型字段，也包括了非元模型字段但标注了Column的字段(key全部存小写)

    /**
     * 是否存在tail
     */
    private ColumnMapping tailField;

    /**
     * 逻辑删除字段.
     */
    private ColumnMapping logicDeleteField;
    /**
     * 是否存在pull方法
     */
    protected boolean existPullMethod;
    /**
     * 是否存在push方法
     */
    protected boolean existPushMethod;

    /**
     * reflectasm相关
     */
    protected ConstructorAccess<?> constructorAccess;
    /**
     * reflectasm相关
     */
    protected MethodAccess methodAccess;

    /**
     * 根据名称得到一个Field对象（大小写不敏感）
     * 如果找不到将返回null(不抛出异常)
     * @param fieldName
     * @return Field对象
     */
    @Override
    public ColumnMapping findField(String fieldName) {
        return getIgnoreCaseDBFieldColumnMapping(fieldName);
    }

    /**
     * 区分大小写获取对应数据库字段的columnMapping
     * @param fieldName
     * @return
     */
    @Override
    public ColumnMapping getStrictCaseDBFieldColumnMapping(String fieldName) {
        DBField field = fields.get(fieldName);
        if (field != null) {
            return schemaMap.get(field);
        }
        return null;
    }

    @Override
    public ColumnMapping getIgnoreCaseDBFieldColumnMapping(String fieldName) {
        for (Map.Entry<String, DBField> entry : fields.entrySet()) {
            if (entry.getKey().equalsIgnoreCase(fieldName) && entry.getValue() != null) {
                return schemaMap.get(entry.getValue());
            }
        }
        return null;
    }

    @Override
    public ColumnMapping getJavaFieldColumnMapping(String fieldName) {
        return metaFieldMap.get(fieldName);
    }

    /**
     * 获取严格区分大小写的字段
     * @param columnName 数据库列名
     * @return
     */
    @Override
    public ColumnMapping getStrictCaseColumnMapping(String columnName) {
        return columnFieldMap.getCaseSensitive(columnName);
    }

    /**
     * 忽略大小写获取对应数据库字段的columnMapping
     * @param columnName 数据库列名
     * @return
     */
    @Override
    public ColumnMapping getIgnoreCaseColumnMapping(String columnName) {
        return columnFieldMap.get(columnName);
    }

    public String getSchema() {
        return schema;
    }

    public void setSchema(String schema) {
        this.schema = schema;
    }

    public String getTableName() {
        return tableName;
    }

    public void setTableName(String tableName) {
        this.tableName = tableName;
    }

    public List<ColumnMapping> getMetaFields() {
        return metaFields;
    }

    public Map<String, DBField> getFields() {
        return fields;
    }

    public Map<String, DBCascadeField> getCascadeFields() {
        return cascadeFields;
    }

    public void setCascadeFields(Map<String, DBCascadeField> cascadeFields) {
        this.cascadeFields = cascadeFields;
    }

    public boolean isCacheable() {
        return cacheable;
    }

    public void setCacheable(boolean cacheable) {
        this.cacheable = cacheable;
    }

    public Class<?> getThisType() {
        return thisType;
    }

    public void setThisType(Class<?> thisType) {
        this.thisType = thisType;
    }

    @Override
    public String getName() {
        return null;
    }

    @Override
    public String getSimpleName() {
        return null;
    }

    public List<ColumnMapping> getSpecialFields() {
        return specialFields;
    }

    public void setSpecialFields(List<ColumnMapping> specialFields) {
        this.specialFields = specialFields;
    }

    public List<ColumnMapping> getPkFields() {
        return pkFields;
    }

    public void setPkFields(List<ColumnMapping> pkFields) {
        this.pkFields = pkFields;
    }

    public Map<DBField, ColumnMapping> getSchemaMap() {
        return schemaMap;
    }

    @Override
    public List<ColumnMapping> getSchemas() {
        return schemas;
    }

    public String getCatalog() {
        return catalog;
    }

    public void setCatalog(String catalog) {
        this.catalog = catalog;
    }

//    public List<UniqueConstraint> getUniques() {
//        return uniques;
//    }
//
//    public void setUniques(List<UniqueConstraint> uniques) {
//        this.uniques = uniques;
//    }
//
//    public List<Index> getIndexs() {
//        return indexs;
//    }
//
//    public void setIndexs(List<Index> indexs) {
//        this.indexs = indexs;
//    }

    public Table getTable() {
        return table;
    }

    public void setTable(Table table) {
        this.table = table;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public Map<String, ColumnMapping> getMetaFieldMap() {
        return metaFieldMap;
    }

    public LinkedCaseInsensitiveMap<ColumnMapping> getColumnFieldMap() {
        return columnFieldMap;
    }

    public Map<DBField, ColumnMapping> getVersionMap() {
        return versionMap;
    }

    public boolean hasMeta(String key) {
        return tableMetaMap.containsKey(key);
    }

    public String getMeta(String key) {
        return tableMetaMap.get(key);
    }

    public Map<String, String> getTableMetaMap() {
        return tableMetaMap;
    }

    public void setTableMetaMap(Map<String, String> tableMetaMap) {
        this.tableMetaMap = tableMetaMap;
    }

    public void setVersionMap(Map<DBField, ColumnMapping> versionMap) {
        this.versionMap = versionMap;
    }

    public boolean isExistPullMethod() {
        return existPullMethod;
    }

    public void setExistPullMethod(boolean existPullMethod) {
        this.existPullMethod = existPullMethod;
    }

    public boolean isExistPushMethod() {
        return existPushMethod;
    }

    public void setExistPushMethod(boolean existPushMethod) {
        this.existPushMethod = existPushMethod;
    }

    public ConstructorAccess<?> getConstructorAccess() {
        return constructorAccess;
    }

    public void setConstructorAccess(ConstructorAccess<?> constructorAccess) {
        this.constructorAccess = constructorAccess;
    }

    public MethodAccess getMethodAccess() {
        return methodAccess;
    }

    public void setMethodAccess(MethodAccess methodAccess) {
        this.methodAccess = methodAccess;
    }

    public ColumnMapping getTailField() {
        return tailField;
    }

    public void setTailField(ColumnMapping tailField) {
        this.tailField = tailField;
    }

    public ColumnMapping getLogicDeleteField() {
        return logicDeleteField;
    }

    public void setLogicDeleteField(ColumnMapping logicDeleteField) {
        this.logicDeleteField = logicDeleteField;
    }
}
